package cn.lanqiao.business.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public class EXLSReader {
  
  
  //对象属性名集合
  static List<String> columnNameList = new ArrayList<>();

  //对象属性类型集合
  static List<String> columnTypeNameList = new ArrayList<>();
  /*
   * @author March
   * @Description  readExcelData:解析excel表格，并且封装进对象；一行就是一个map，map的key是对象的属性名；属性的数量和位置必须和表的列一一对应
   * @warning:Java对象的属性顺序还有数量必须和excel的列的顺序一一对应，否则数据不对应；Java属性的id可以忽略
   * @Date 1:36 下午 2020/5/19
   * @param url:excel文件地址
   * @param readSheetName ：表名
   * @param ignoreRow：需要忽略的行数，如标题这种
   * @param titleRow：列名，作为对象的key
   * @return
   **/
  public static <T> List<Map<String, Object>> readExcelData(String url, String readSheetName, Integer ignoreRow, Class<T> clz) throws Exception {

      Optional.ofNullable(url).orElseThrow(() -> new RuntimeException("url文件路径不能为空"));
      Optional.ofNullable(readSheetName).orElseThrow(() -> new RuntimeException("readSheetName文件路径不能为空"));

      // 从XLSX/ xls文件创建的输入流
      try {
          FileInputStream fis = new FileInputStream(url);
          //封装单元格参数，map就是一行数据，key是
          List<Map<String, Object>> hospitalList = new ArrayList();

          // 创建工作薄Workbook
          Workbook workBook = null;

          // 读取2007版，以    .xlsx 结尾
          if (url.toLowerCase().endsWith("xlsx")) {
              try {
                  workBook = new XSSFWorkbook(fis);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          // 读取2003版，以   .xls 结尾
          if (url.toLowerCase().endsWith("xls")) {
              try {
                  workBook = new HSSFWorkbook(fis);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          //获取总共的表格数量
          int numberOfSheets = workBook.getNumberOfSheets();

          // 循环 numberOfSheets
          for (int sheetNum = 0; sheetNum < numberOfSheets; sheetNum++) {

              // 得到 工作薄 的第 N个表
              Sheet sheet = workBook.getSheetAt(sheetNum);
              String sheetName = sheet.getSheetName();
              if (readSheetName.equals(sheetName)) {
                  //行
                  Row row;
                  // cell就是每个单元格的值
                  String cell;

                  //按顺序获取class的属性的类型的字符串
                  columnTypeNameList=EXLSReader.parseBeanPropertyTypeName(clz);
                  //按顺序获取class的属性名称
                  columnNameList=EXLSReader.paresBeanPropertyName(clz);

                  //数据封装
                  for (int i = sheet.getFirstRowNum(); i < sheet.getPhysicalNumberOfRows(); i++) {
                      //忽略的行数；
                      if (Integer.compare(i, ignoreRow - 1) != 1) {
                          continue;
                      }

                      // 循环行数
                      row = sheet.getRow(i);

                      //存储每行的参数
                      Map<String, Object> map = new HashMap<>();
                      for (int j = row.getFirstCellNum(); j < row.getPhysicalNumberOfCells(); j++) {

                          //遍历列名集合，
                          for (int k = 0; k < columnNameList.size(); k++) {
                              //当列名集合的下表和这行的下表相等时就放入map
                              if (k == j) {
                                  cell=Optional.ofNullable(row.getCell(j).toString()).orElse("");

                                  switch (columnTypeNameList.get(k)){
                                      case "java.lang.Int":
                                          map.put(columnNameList.get(k),Integer.valueOf(StringUtils.contains(cell,".")?cell.substring(0,cell.indexOf(".")):cell));
                                          break;
                                      case "java.lang.Long":
                                          map.put(columnNameList.get(k),Long.valueOf(cell));
                                          break;
                                      case "java.lang.Double":
                                          map.put(columnNameList.get(k),Double.valueOf(cell));
                                          break;
                                      case "java.lang.Float":
                                          map.put(columnNameList.get(k),Float.valueOf(cell));
                                          break;
                                      case "java.math.BigInteger":
                                          map.put(columnNameList.get(k), BigInteger.valueOf(Long.valueOf(cell)));
                                          break;
                                      case "java.math.BigDecimal":
                                          map.put(columnNameList.get(k),BigDecimal.valueOf(Double.valueOf(cell)));
                                          break;
//                                      case "java.util.Date":
//                                          map.put(columnNameList.get(k), DateUtils.parseDate(cell,"yyyy年MM月dd日","yyyy-MM-dd hh:mm:ss","yyyy-MM-dd HH:mm:ss"));
//                                          break;
                                      //默认返回字符串
                                      //如果有需要的拓展类型，在上面的case中增加类型的全类路径名
                                      default:
                                          map.put(columnNameList.get(k),cell);
                                          break;
                                  }
                              }
                          }
//                          System.out.print(cell+"\t");
                      }
                      hospitalList.add(map);
//                      System.out.println();
                  }
              }

          }
          return hospitalList;
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
  }

  /*
   * @author March
   * @Description  parseMapListToObjectList：把excel的文件读出，先转为map集合，再转为对象集合
   * @Warning：对象的属性名的顺序数量必须和excel表格一一对应
   * @Date 1:33 下午 2020/5/21
       * @param url：excel文件路径
       * @param readSheetName：表名
       * @param ignoreRow：忽略的行数（只需要数据，不需要列的字段）
       * @param clz：需要转换的对象的class
   * @return
  **/
  public static <T> List<T> parseExcelToMapListToObjectList(String url, String readSheetName, Integer ignoreRow, Class<T> clz) throws IllegalAccessException, InstantiationException, IntrospectionException {
      List<T> listInfo = new ArrayList<>();
      List<Map<String, Object>> list = null;
      try {
          list = readExcelData(url, readSheetName, ignoreRow, clz);
      } catch (Exception e) {
          e.printStackTrace();
      }
      list.forEach(map -> {
          try {
              //map转为对象并且加入集合
              listInfo.add(EXLSReader.map2bean(map,clz));
          } catch (Exception e) {
              e.printStackTrace();
          }
      });
      return listInfo;
  }


  /*
   * @author March
   * @Description  map2bean：把map的值转为对象
   * @Warning：对象的属性名和map的key必须一致
   * @Date 1:35 下午 2020/5/21
   * @param map
       * @param clz
   * @return
  **/
  public static <T> T map2bean(Map<String, Object> map, Class<T> clz) throws Exception {
      if (null == map) {
          throw new RuntimeException();
      }
      try {
          //创建一个需要转换为的类型的对象
          T obj = clz.newInstance();
          //从Map中获取和属性名称一样的值，把值设置给对象(setter方法)

          //得到属性的描述器
          BeanInfo b = Introspector.getBeanInfo(clz, Object.class);
          PropertyDescriptor[] pds = b.getPropertyDescriptors();
          for (PropertyDescriptor pd : pds) {
              //得到属性的setter方法
              Method setter = pd.getWriteMethod();
              //得到key名字和属性名字相同的value设置给属性
              String name = pd.getName();
              Object o = map.get(name);
              if(o==null || o.toString().equals("")){
                  break;
              }
              if("finalScore".equals(name) || "priScore".equals(name)){

                  double i = Double.parseDouble(o.toString());
                  setter.invoke(obj, i);
              }else{
                  setter.invoke(obj, map.get(name));
              }
          }
          return obj;
      } catch (Exception e) {
          throw new RuntimeException();
      }
  }


  /*
   * @author March
   * @Description  paresBeanPropertyName:根据class依序获取对象属性的名字
   * @Date 11:13 上午 2020/5/21
   * @param clz
   * @return
  **/
  public static <T> List<String> paresBeanPropertyName(Class<T> clz) {
      //获取class的属性字段；
      Field[] fields = clz.getDeclaredFields();
      if (fields.length > 0) {
          List<String> beanPropertyNames=new ArrayList<>();
          try {
              //在使用java反射机制获取 JavaBean 的属性值时，如果该属性被声明为private 的，需要将setAccessible设置为true. 默认的值为false
              Field.setAccessible(fields, true);
              for (int i = 0; i < fields.length; i++) {
                  //忽略ID字段
                  if("id".equals(fields[i].getName())){
                      continue;
                  }
                  //获取属性的名字
                  beanPropertyNames.add(fields[i].getName());
              }
              return beanPropertyNames;
          }catch (Exception e){
              e.printStackTrace();
          }
      }
      return null;
  }



  /*
   * @author March
   * @Description  parseBeanPropertyType:根据class依序获取对象属性的数据类型
   * @Date 10:32 上午 2020/5/21
   * @param clz
   * @return
   **/
  public static  <T> List<String> parseBeanPropertyTypeName(Class<T> clz) {
      Field[] fields = clz.getDeclaredFields();
      if (fields.length > 0) {
          List<String> beanPropertyTypes=new ArrayList<>();
          try {
              //在使用java反射机制获取 JavaBean 的属性值时，如果该属性被声明为private 的，需要将setAccessible设置为true. 默认的值为false
              Field.setAccessible(fields, true);
              for (int i = 0; i < fields.length; i++) {
                  //获取属性的类型的名字
                  //忽略ID字段
                  if("id".equals(fields[i].getName())){
                      continue;
                  }
                  beanPropertyTypes.add(fields[i].getType().getName());
              }
              return beanPropertyTypes;
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      return null;
  }

  public static boolean isNumeric(String str) {
      try {
          new BigDecimal(str);
      } catch (Exception e) {
          return false;
      }
      return true;
  }

}